#include "mainwindow.h"

#include "ui_mainwindow.h"

MainWindow::MainWindow( int argc, char** argv, QWidget* parent ) : QMainWindow( parent ), ui( new Ui::MainWindow ), user_( User() ), argc( argc ), argv( argv ) {
    ui->setupUi( this );
    // UPDATE 显示版本号 “代码版本+日期”
    edition = "LHAGV-V0.0(宿迁阿特斯)";
    Update  = "2023/06/17";

#ifdef __DEBUG__
    user_ = User( "调试模式", "000", User::DEBUG_MODE );
#endif
}

MainWindow::~MainWindow() {
    thread_1->quit();
    delete ui;
}

Maindeal* MainWindow::MainDeal() {
    return maindeal;
}

/**
 * @brief MainWindow::initUI    初始化软件界面
 */
void MainWindow::initUI() {                                         //  MainWindow 初始化界面
    this->setWindowIcon( QIcon( ":/pic/src/logo/app_logo.ico" ) );  // 设置程序图标
    /********************************* 界面设置 *********************************************/
    this->setWindowTitle( "AGV控制系统" );
    ui->textBrowser->document()->setMaximumBlockCount( 500 );                 // 主界面消息框
    ui->textBrowser_2->document()->setMaximumBlockCount( 500 );               // 手动界面消息框
    ui->textBrowser_3->document()->setMaximumBlockCount( 500 );               // 手动界面消息框
    ui->textBrowser_system_order->document()->setMaximumBlockCount( 500 );    // 数据界面消息框
    ui->textBrowser_wireless_order->document()->setMaximumBlockCount( 500 );  // 各模块数据 UPDATE 2022.10.28 减少testbrowser的数据储存量为500
    /******************************** 界面部件美化 ******************************************/
    initWidget();
    checkAndSetAuthority();
    /******************************** 设置默认tab页 ****************************************/
    ui->tabWidget->setCurrentIndex( 0 );
    /******************************** 设置地图管理页 ****************************************/
    RViz::createRVizFrame( this, ui->rviz_layout );
}

/**
 * @brief MainWindow::initData 初始化数据
 */
void MainWindow::initData() {
    Param::readParam();  // First thing's first.读取参数表
    Param::printParam();

    /*********************************** agv初始化 *****************************************/
    thread_1 = new QThread;
    maindeal = new Maindeal( argc, argv );
    maindeal->moveToThread( thread_1 );
    connect( maindeal, SIGNAL( sigSendTips( QString ) ), this, SLOT( sltShowTips( QString ) ) );
    connect( maindeal, SIGNAL( sigRecvSystemOrder( QByteArray, QString ) ), this, SLOT( sltShowSystemOrder( QByteArray, QString ) ) );
    //  4.14 充电桩无通讯，不再占用临时数据窗口
    //  connect( maindeal, SIGNAL( sigSendChargeOrder( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );
    connect( thread_1, SIGNAL( started() ), maindeal, SLOT( sltMaindealInit() ) );
    connect( thread_1, SIGNAL( finished() ), thread_1, SLOT( deleteLater() ) );
    thread_1->start();
    thread_1->setPriority( QThread::TimeCriticalPriority );
    /*********************************** UdpSocket *****************************************/
    udp_socket = new UDPUtils( Param::agv_ip, Param::agv_sys_msg_port, true, this );
    connect( udp_socket, SIGNAL( sigSendTips( QString, QString ) ), this, SLOT( sltShowSysTips( QString, QString ) ) );  // UPDATE 2022.07.28 文字显示框，输出发送方ip
    udp_socket->sltOpenUDP();
    // 界面扫描时候，多个变量为空指针，需要等待对象构建完成。电脑处理速度不同可以适当调整
    Utility::blockDelayMs( 500 );  // 阻塞延时执行

    /******************************* 定时器初始化 *******************************************/
    QTimer* timer50ms = new QTimer();
    connect( timer50ms, SIGNAL( timeout() ), this, SLOT( slt50msTimer() ) );
    timer50ms->start( 50 );

    QTimer* timer500ms = new QTimer();
    connect( timer500ms, SIGNAL( timeout() ), this, SLOT( slt500msTimer() ) );
    timer500ms->start( 500 );

    QTimer* timer1000ms = new QTimer();
    connect( timer1000ms, SIGNAL( timeout() ), this, SLOT( slt1000msTimer() ) );
    timer1000ms->start( 1000 );

    QTimer* timer30s = new QTimer();
    connect( timer30s, SIGNAL( timeout() ), this, SLOT( slt30sTimer() ) );
    timer30s->start( 30 * 1000 );

    // 如果连续开机,那么每24小时执行一次
    QTimer* clearLogTimer = new QTimer();
    connect( clearLogTimer, SIGNAL( timeout() ), this, SLOT( sltClearLog() ) );
    clearLogTimer->start( 1000 * 60 * 60 * 24 );

    /******************************* IO界面初始化 ******************************************/
    connect( maindeal->agvhardware()->ioModule(), SIGNAL( sigSendIoState( QBitArray ) ), ui->widget_io_state, SLOT( slotRecvIoData( QBitArray ) ) );
    connect( maindeal->agvhardware()->ioModule(), SIGNAL( sigSendIoType( QBitArray ) ), ui->widget_io_state, SLOT( sltSetIoType( QBitArray ) ) );
    connect( ui->widget_io_state, SIGNAL( sigToggleDoBit( int, int ) ), maindeal->agvhardware()->ioModule(), SLOT( sltToggleDoBit( int, int ) ) );
    maindeal->agvhardware()->ioModule()->sendIoType();
    // UPDATE 2022.07.14 改栏目中文
    QStringList items;  //各模块接收数据在数据表中显示，根据调试需要选择
    items << "选择需要监控的模块"
          << "IO模块数据"
          << "发送调度数据"
          << "CAN报文数据"
          << "BatteryCAN报文数据"
          << "充电桩数据";

    //        << "电池模块数据"

    ui->comboBox_chooese_hardware->addItems( items );
    ui->comboBox_chooese_hardware->setCurrentIndex( 0 );
    connect( ui->comboBox_chooese_hardware, SIGNAL( currentIndexChanged( int ) ), this, SLOT( sltChangeModule( int ) ) );
    // 开启先执行一次，日志清楚检测  << "二维码模块数据"
    sltClearLog();
}

void MainWindow::showBatteryData() {
    auto battery_data = maindeal->agvhardware()->Battery_Ropeencoder_canModule()->getBatteryData();

    switch ( battery_data.charge_state ) {
    case Battery_Ropeencoder_can::Uncharge:
        // 动画电量格数固定，并按照百分比设定。
        ui->battary_widget_1->showChargeAnimate( false );
        ui->battary_widget_2->showChargeAnimate( false );
        ui->battary_widget_1->setValue( battery_data.soc );
        ui->battary_widget_2->setValue( battery_data.soc );
        // 显示百分比数值
        ui->battary_widget_1->setPercentValue( battery_data.soc );
        ui->battary_widget_2->setPercentValue( battery_data.soc );
        ui->label_battery_state->setText( "正在放电" );
        ui->label_battery_state->setStyleSheet( green_style_ );
        break;
    case Battery_Ropeencoder_can::Charging:
        // 动画电量格数不固定，按照充电滚动。
        if ( battery_data.total_cur > 0 )
        //判断充电时电流为正数大于0，才为充电。充电动画显示
        {
            ui->battary_widget_1->showChargeAnimate( true );
            ui->battary_widget_2->showChargeAnimate( true );

            // 显示百分比数值
            ui->battary_widget_1->setPercentValue( battery_data.soc );
            ui->battary_widget_2->setPercentValue( battery_data.soc );
            ui->label_battery_state->setText( "正在充电" );
            ui->label_battery_state->setStyleSheet( orange_style_ );
        }
        break;
    case Battery_Ropeencoder_can::Charged:
        // 动画电量格数固定，并按照百分比设定。
        ui->battary_widget_1->showChargeAnimate( false );
        ui->battary_widget_2->showChargeAnimate( false );
        ui->battary_widget_1->setValue( battery_data.soc );
        ui->battary_widget_2->setValue( battery_data.soc );
        // 显示百分比数值
        ui->battary_widget_1->setPercentValue( battery_data.soc );
        ui->battary_widget_2->setPercentValue( battery_data.soc );
        ui->label_battery_state->setText( "充电完成" );
        ui->label_battery_state->setStyleSheet( green_style_ );
        break;
    }

    if ( battery_data.charge_relay_state ) { ui->label_battery_fault_state->setText( "继电器闭合" ); }
    else {
        ui->label_battery_fault_state->setText( "无状态" );
    }

    //基本参数
    ui->ld_ALL_CELL_VOLTAGE->setText( QString( "%1V" ).arg( battery_data.total_vol ) );
    ui->ld_MEASURING_CURRENT->setText( QString( "%1A" ).arg( battery_data.total_cur ) );
    ui->ld_SOC->setText( QString( "%1%" ).arg( battery_data.soc ) );
    ui->ld_MAX_VOLTAGE->setText( QString( "%1V" ).arg( battery_data.max_singel_vol ) );
    ui->ld_MAX_VOLTAGE_NUM->setText( QString( "%1" ).arg( battery_data.max_singel_vol_num ) );
    ui->ld_MIN_VOLTAGE->setText( QString( "%1V" ).arg( battery_data.min_singel_vol ) );
    ui->ld_MIN_VOLTAGE_NUM->setText( QString( "%1" ).arg( battery_data.min_singel_vol_num ) );
    ui->ld_MAX_TEMP_TEMP_EXCURSION_VALUE->setText( QString( "%1" ).arg( battery_data.max_temp ) );
    ui->ld_MAX_TEMP_EXCURSION_NUM->setText( QString( "%1" ).arg( battery_data.max_temp_num ) );
    ui->ld_MIN_TEMP_TEMP_EXCURSION_VALUE->setText( QString( "%1" ).arg( battery_data.min_temp ) );
    ui->ld_MIN_TEMP_EXCURSION_NUM->setText( QString( "%1" ).arg( battery_data.min_temp_num ) );
}

/**
 * @brief MainWindow::slot50msTimer 槽函数.50ms定时器中断处理.
 */
void MainWindow::slt50msTimer() {
    showSpeed();
    showNaviInfo();
    if ( is_open_record_route ) {
        int    x      = static_cast< int >( maindeal->navigator()->slamPoint().x() );
        int    y      = static_cast< int >( maindeal->navigator()->slamPoint().y() );
        double radian = maindeal->navigator()->slamPoint().radian();
        emit   sigSendCurPoint( x, y, radian );
        //    emit sigSendQrocdePoint(maindeal->navigator()->qrcodePoint(), maindeal->navigator()->getQrcodeId());
    }

    switch ( maindeal->agvhardware()->getGoodsState() ) {
    case Agvhardware::EMPTY:
        ui->label_agv_goods_state->setText( "<font color='green'>空载</font>" );
        break;
    case Agvhardware::PALLET:
        ui->label_agv_goods_state->setText( "<font color='orange'>空托盘</font>" );
        break;
    case Agvhardware::PALLET_AND_GOODS:
        ui->label_agv_goods_state->setText( "<font color='red'>货物</font>" );
        break;
    }

    switch ( maindeal->agvhardware()->getForkPosState() ) {
    case Agvhardware::FORK_TOP:
        ui->label_agv_fork_state->setText( "<font color='green'>上限位</font>" );
        break;
    case Agvhardware::FORK_BOTTOM:
        ui->label_agv_fork_state->setText( "<font color='green'>下限位</font>" );
        break;
    case Agvhardware::FORK_RAISING:
        ui->label_agv_fork_state->setText( "<font color='orange'>上升中</font>" );
        break;
    case Agvhardware::FORK_FALLING:
        ui->label_agv_fork_state->setText( "<font color='orange'>下降中</font>" );
        break;
    case Agvhardware::FORK_UNKNOWN:
        ui->label_agv_fork_state->setText( "<font color='red'>未知</font>" );
        break;
    }

    //  ui->label_agv_good_up_l_state->setText(upper_tips_left);

    //  ui->label_agv_good_down_l_state->setText(lower_tips_left);
}
/**
 * @brief MainWindow::slot500msTimer 槽函数.50ms定时器中断处理.
 */
void MainWindow::slt500msTimer() {
    //显示用户
    QString str_user;
    if ( user_.authority() == User::NOT_LOGIN ) { str_user = "用户: <font color=\"red\">" + user_.username() + "</font>"; }
    else {
        str_user = "用户: <font color=\"blue\">" + user_.username() + "</font>";
    }
    ui->label_user->setText( str_user );
}
/**
 * @brief MainWindow::slt1000msTimer 槽函数,1000ms定时器中断处理
 */
void MainWindow::slt1000msTimer() {
    //显示时间
    ui->label_time->setText( QDateTime::currentDateTime().toString( "yyyy-MM-dd hh:mm:ss dddd" ) );
    // UPDATE 2022.11.05 修改版本显示信息,加入到位光感使用情况
    QString editionStr = "<font color=\"blue\">" + edition + "</font>";
    QString UpdateStr  = "<font color=\"green\">" + Update + "</font>";
    QString systeamStr = "<font color=\"blue\">Ubuntu 16.04 </font>";
    // QString arriveSig = (Param::arrive_signal_open_close == 0) ? "<font color=\"green\">到位光感功能已启用</font>" : "<font color=\"red\">注意！到位光感功能未启用！</font>";
    ui->label_about->setText( /*arriveSig +*/ " ---- 系统:" + systeamStr + "   版本:" + editionStr + "    更新日期:" + UpdateStr + " " );

    /****************主界面设备位置消息框********************/
    //获取信息
    // int firstId = maindeal->navigator()->slamRoutePlanning()->getSlamRouteFirstId( maindeal->navigator()->getSlamRoute() );
    int  lastId      = maindeal->navigator()->slamRoutePlanning()->getSlamRouteLastId( maindeal->navigator()->getSlamRoute() );
    int  curId       = maindeal->navigator()->getSlamId();
    int  curRoute    = maindeal->navigator()->getSlamRoute();
    int  StartId     = maindeal->navigator()->getSlamStartId();
    bool isAutoRoute = maindeal->navigator()->isAutoSetRoute();
    //设置样式  UPDATE 2022.06.25 设定路线方式显示
    QString model       = ( isAutoRoute ) ? "<font color=\"blue\">自动</font>" : "<font color=\"red\">人工</font>";
    QString curRouteStr = "<font color=\"blue\">" + QString::number( curRoute ) + "</font>";
    QString curIdStr    = "<font color=\"blue\">" + QString::number( curId ) + "</font>";
    QString curIdMsg    = "<font color=\"blue\">" + maindeal->navigator()->slamRoutePlanning()->getMachineMsg( curId ) + "</font>";
    QString startIdStr  = "<font color=\"orange\">" + QString::number( StartId ) + "</font>";
    QString startIdMsg  = "<font color=\"orange\">" + maindeal->navigator()->slamRoutePlanning()->getMachineMsg( StartId ) + "</font>";
    QString lastIdStr   = "<font color=\"orange\">" + QString::number( lastId ) + "</font>";
    QString lastIdMsg   = "<font color=\"orange\">" + maindeal->navigator()->slamRoutePlanning()->getMachineMsg( lastId ) + "</font>";
    //起始ID\起始位置显示    //UPDATE 2022.07.21 修改重定位
    QString initposMsg = ( maindeal->navigator()->isInitPose() ) ? "<font color=\"blue\">已重定位!</font>" : "<font color=\"red\">未重定位!</font>";  //  4.6 界面显示定位情况
    ui->label_cur_route->setText( QString( "当前路线:(%1) 设定方式:(%2) 定位:(%3)" ).arg( curRouteStr ).arg( model ).arg( initposMsg ) );
    ui->label_cur_id_and_pos->setText( QString( "当前ID:(%1) 当前位置:(%2)" ).arg( curIdStr ).arg( curIdMsg ) );
    ui->label_start_id_and_pos->setText( QString( "起始ID:(%1) 起始位置:(%2)" ).arg( startIdStr ).arg( startIdMsg ) );
    ui->label_tag_id_and_pos->setText( QString( "目标ID:(%1) 目标位置:(%2)" ).arg( lastIdStr ).arg( lastIdMsg ) );
    /****************主界面设备位置消息框********************/
    //显示agvID
    ui->label_agv_id->setText( QString( "%1号" ).arg( Param::agv_id ) );
    ui->label_agv_id->setStyleSheet( green_style_ );
    //显示agv状态
    // TEST 修改传输类型8：传输状态显示

    switch ( maindeal->agvhardware()->getAgvState() ) {
    case Agvhardware::AGV_RUNNING:
        ui->label_agv_state->setText( tr( "行驶中" ) );
        ui->label_agv_state->setStyleSheet( orange_style_ );
        break;
    case Agvhardware::AGV_ARRIVE:
        ui->label_agv_state->setText( tr( "到达" ) );
        ui->label_agv_state->setStyleSheet( green_style_ );
        break;
    case Agvhardware::AGV_FORK:
        ui->label_agv_state->setText( tr( "升降操作中" ) );
        ui->label_agv_state->setStyleSheet( red_style_ );
        break;
    //    case Agvhardware::AGV_ALLOW_TRANSMIT:
    //      ui->label_agv_state->setText(tr("允许升降"));
    //      ui->label_agv_state->setStyleSheet(orange_style_);
    //      break;
    case Agvhardware::AGV_LIFT_STOP:
        ui->label_agv_state->setText( tr( "强制升降暂停" ) );
        ui->label_agv_state->setStyleSheet( red_style_ );
        break;
    case Agvhardware::AGV_CHARGING:
        ui->label_agv_state->setText( tr( "充电中" ) );
        ui->label_agv_state->setStyleSheet( orange_style_ );
        break;
    case Agvhardware::AGV_PRECHARGE:
        ui->label_agv_state->setText( tr( "准备充电" ) );
        ui->label_agv_state->setStyleSheet( orange_style_ );
        break;
    case Agvhardware::AGV_TRAFFIC:
        ui->label_agv_state->setText( tr( "交管中" ) );
        ui->label_agv_state->setStyleSheet( orange_style_ );
        break;
    case Agvhardware::AGV_STOP:
        ui->label_agv_state->setText( tr( "停止" ) );
        ui->label_agv_state->setStyleSheet( red_style_ );
        break;
    case Agvhardware::AGV_OBSTACLE:
        ui->label_agv_state->setText( tr( "停障" ) );
        ui->label_agv_state->setStyleSheet( red_style_ );
        break;
    case Agvhardware::AGV_EMERGENCY:
        ui->label_agv_state->setText( tr( "急停中" ) );
        ui->label_agv_state->setStyleSheet( red_style_ );
        break;
    }

    // 显示agv模式
    if ( maindeal->navigator()->isAutoModeEnable() ) {
        ui->label_agv_mode->setText( tr( "自动模式" ) );
        ui->label_agv_mode->setStyleSheet( orange_style_ );
    }
    else if ( !maindeal->navigator()->isAutoModeEnable() ) {
        ui->label_agv_mode->setText( tr( "手动模式" ) );
        ui->label_agv_mode->setStyleSheet( green_style_ );
    }

    //展示电池数据
    showBatteryData();

    //自动解除登录
    if ( user_.authority() != User::NOT_LOGIN ) {
        int        count = 30 * 60;
        static int time  = count;
        if ( ( user_.authority() == User::SUPER_ADMIN ) || ( user_.authority() == User::DEBUG_MODE ) ) {
            if ( /*--*/ time <= 0 ) {
                this->showFullScreen();
                on_pushButton_logout_clicked();
                if ( !maindeal->navigator()->isAutoModeEnable() ) {
                    sltShowTips( "非自动模式，停车" );
                    maindeal->navigator()->EmergencyStop();
                }
                time = count;
            }
            if ( time <= 10 ) { sltShowTips( QString( "自动退出用户倒计时：%1" ).arg( time ) ); }
        }
        else {
            time = count;
        }
    }
    // UPDATE 2022.06.05 自动模式下，每隔周期切全屏
    int        count2 = 5 * 60;
    static int time2  = count2;
    if ( maindeal->navigator()->isAutoModeEnable() ) {
        if ( --time2 <= 0 ) {
            this->showFullScreen();
            time2 = count2;
        }
    }
    else {
        time2 = count2;
    }
}

void MainWindow::slt30sTimer() {
    QString     updataName = "./ini/updata.csv";
    QFile       updataFile( updataName );
    QStringList csvList;
    csvList.clear();

    if ( !updataFile.exists() ) {
        qCritical() << "错误，无法找到该路线文件:" + updataName;
        return;
    }

    //对csv文件进行读写操作
    if ( updataFile.open( QIODevice::ReadWrite ) ) {
        QTextStream stream( &updataFile );
        while ( !stream.atEnd() ) {
            csvList.push_back( stream.readLine().replace( "\"", "" ) );
        }  //保存到List当中
        updataFile.close();
    }
    else {
        qCritical() << "错误，打开文件失败:" + updataName;
        return;
    }

    //存到obs_model
    if ( csvList.size() != 0 ) {
        QStringList valsplit0 = csvList.at( 0 ).split( "," );  //分隔字符串
        if ( valsplit0.at( 0 ) != "enable" ) {
            qCritical() << "第0行格式错误:" << updataName;
            return;
        }
        if ( valsplit0.at( 1 ).toInt() == 1 ) {                    // enable==1
            QStringList valsplit1 = csvList.at( 1 ).split( "," );  //分隔字符串
            if ( valsplit1.at( 0 ) != "slam" || valsplit1.at( 1 ) != "qrcode" || valsplit1.at( 2 ) != "param" ) {
                qCritical() << "第1行格式错误:" << updataName;
                return;
            }

            QStringList valsplit2 = csvList.at( 2 ).split( "," );  //分隔字符串
            if ( valsplit2.at( 0 ).toInt() == 1 ) {                // slam重新读取
                sltShowTips( "系统自动:重新读取激光路线文件!" );
                maindeal->navigator()->slamRoutePlanning()->allFileReset();
            }
            if ( valsplit2.at( 1 ).toInt() == 1 ) {  // qrcde重新读取

                //        sltShowTips("系统自动:重新读取二维码路线文件!");
                //        maindeal->navigator()->coordianteRoutePlanning()->allFileReset();
            }
            if ( valsplit2.at( 2 ).toInt() == 1 ) {  // param重新读取

                //        sltShowTips("系统自动:参数初始化成功!");
                //        Param::readParam();
                //        maindeal->navigator()->initPid();
            }
            updataFile.remove();  //删除
            QFile newUpdataFile;
            newUpdataFile.setFileName( updataName );
            newUpdataFile.open( QIODevice::WriteOnly | QIODevice::Append );
            QTextStream text_stream( &newUpdataFile );
            if ( newUpdataFile.size() == 0 ) {  //新文件
                text_stream << "enable"
                            << ","
                            << "0"
                            << "\r\n"
                            << "slam"
                            << ","
                            << "qrcode"
                            << ","
                            << "param"
                            << "\r\n"
                            << "0"
                            << ","
                            << "0"
                            << ","
                            << "0"
                            << "\r\n";
            }
            sltShowTips( "系统自动:更新文件复位完成!" );
        }
    }
}

/**
 * @brief MainWindow::sltClearLog 槽函数，清除日志文件
 */
void MainWindow::sltClearLog() {
    QDir* info_dir     = new QDir( "./info-log/" );
    QDir* debug_dir    = new QDir( "./debug-log/" );
    QDir* crittcal_dir = new QDir( "./crittcal-log/" );

    QDateTime dead_time = QDateTime::currentDateTime().addDays( -data_min_time );

    for ( auto info_file : info_dir->entryInfoList() ) {
        if ( info_file.lastModified() < dead_time ) {
            QFile* file = new QFile( info_file.filePath() );
            qDebug() << "remove " << info_file.filePath();
            file->remove();
        }
    }

    for ( auto debug_file : debug_dir->entryInfoList() ) {
        if ( debug_file.lastModified() < dead_time ) {
            QFile* file = new QFile( debug_file.filePath() );
            qDebug() << "remove " << debug_file.filePath();
            file->remove();
        }
    }

    for ( auto crittcal_file : crittcal_dir->entryInfoList() ) {
        if ( crittcal_file.lastModified() < dead_time ) {
            QFile* file = new QFile( crittcal_file.filePath() );
            qDebug() << "remove " << crittcal_file.filePath();
            file->remove();
        }
    }
}

/**
 * @brief MainWindow::showSpeed 展示速度值到主界面上，并且使用BlinkLabel颜色切换来实现左右转区分
 */
void MainWindow::showSpeed() {
    double v, a;

    v = maindeal->navigator()->get_Fork_WalkRealitySpeed();
    a = maindeal->navigator()->get_Fork_VeerRealityRad();
    ui->label_cur_left_speed->setText( tr( "舵轮:%1m/s" ).arg( v, 0, 'f', 2 ) );
    ui->label_cur_left_speed_2->setText( tr( "舵轮:%1m/s" ).arg( v, 0, 'f', 2 ) );
    ui->label_cur_right_speed->setText( tr( "角度:%1°" ).arg( a * 180 / M_PI, 0, 'f', 2 ) );
    ui->label_cur_right_speed_2->setText( tr( "角度:%1°" ).arg( a * 180 / M_PI, 0, 'f', 2 ) );
}

/**
 * @brief MainWindow::showNaviInfo 界面显示导航信息
 */
void MainWindow::showNaviInfo() {
    // UPDATE 2022.08.11 重新GUI中显示中文数据，ui字体改11
    ui->label_cur_speed->setText( QString( "设定速度:%1m/s 设定角度:%2° 实际速度:%3m/s 实际角度:%4° " )
                                      .arg( maindeal->navigator()->get_Fork_WalkSettingSpeed(), 0, 'f', 3 )
                                      .arg( maindeal->navigator()->get_Fork_VeerSettingRad() * 180 / M_PI, 0, 'f', 1 )
                                      .arg( maindeal->navigator()->get_Fork_WalkRealitySpeed(), 0, 'f', 3 )
                                      .arg( maindeal->navigator()->get_Fork_VeerRealityRad() * 180 / M_PI, 0, 'f', 1 ) );
    /****************************************根据导航模式，显示对应的参数*********************************/
    Navigator::NavigationMode navimode = maindeal->navigator()->getNavigationMode();
    //  CoordinateNavigation::NaviStep coordNavistep = maindeal->navigator()->coordinateNavigation()->getCoordiantNaviStep();
    //  CoordinateNavigation::NaviStep slamNavistep = maindeal->navigator()->getSlamNaviStep();
    //  //默认显示激光的Navistep
    //  CoordinateNavigation::NaviStep navistep = (navimode == Navigator::NAVIGATION_COOR) ? coordNavistep : slamNavistep;
    //  //根据导航模式，显示对应的naviparam
    //  double erad, eh, edis;
    //  if (navimode == Navigator::NAVIGATION_COOR) {
    //    erad = maindeal->navigator()->coordinateNavigation()->getRadErrStart2Target();
    //    eh = maindeal->navigator()->coordinateNavigation()->getPointTolineDistance();
    //    edis = maindeal->navigator()->coordinateNavigation()->getDisCur2Target();
    //  } else {
    //    maindeal->navigator()->getSlamParam(erad, eh, edis);
    //  }
    Navigator::NaviStep navistep = maindeal->navigator()->getSlamNaviStep();
    double              erad, eh, edis;
    maindeal->navigator()->getSlamParam( erad, eh, edis );
    /****************************************根据导航模式，显示对应的参数*********************************/
    ui->label_navi_state->setText( QString( " station:%1->%2 %3 弧度差:%4 距离差:%5mm 垂直距离差:%6mm " )
                                       .arg( maindeal->navigator()->stations_list().front().id )
                                       .arg( maindeal->navigator()->stations_list().back().id )
                                       .arg( QMetaEnum::fromType< Navigator::NaviStep >().valueToKey( navistep ) )
                                       .arg( erad, 0, 'f', 1 ) /* * 180 / M_PI*/  // TODO将雷达计算弧度转为角度
                                       .arg( edis, 0, 'f', 0 )
                                       .arg( eh, 0, 'f', 0 ) );

    int front, back;  // 3.29 界面显示obs状态
    maindeal->navigator()->getObsState( front, back );
    ui->label_senser_position->setText( QString( " 激光坐标 x:%1mm y:%2mm rad:%3 避障 左:%4 右:%5 左:%6 右:%7" )  //
                                            .arg( maindeal->navigator()->slamPoint().x(), 0, 'f', 0 )
                                            .arg( maindeal->navigator()->slamPoint().y(), 0, 'f', 0 )
                                            .arg( maindeal->navigator()->slamPoint().radian(), 0, 'f', 4 )  // * 180 / M_PI, 0,
                                            .arg( front )
                                            .arg( back )
                                            .arg( maindeal->agvhardware()->get_l_obs_open() ? "开" : "关" )  // UPDATE 2022.11.01 显示左右避障开关
                                            .arg( maindeal->agvhardware()->get_r_obs_open() ? "开" : "关" ) );

    ui->label_cur_position->setText( QString( " 计算坐标 x:%1mm y:%2mm yaw:%3°" )  // 二维码坐标 x:%4 y:%5 rad:%6 id:%7 ")
                                         .arg( maindeal->navigator()->curPoint().x(), 0, 'f', 0 )
                                         .arg( maindeal->navigator()->curPoint().y(), 0, 'f', 0 )
                                         .arg( maindeal->navigator()->curPoint().radian() * 180 / M_PI, 0, 'f', 1 )
                                     // .arg(maindeal->navigator()->qrcodePoint().x())
                                     // .arg(maindeal->navigator()->qrcodePoint().y())
                                     // .arg(maindeal->navigator()->qrcodePoint().radian(), 0, 'f', 3)
                                     // .arg(maindeal->navigator()->getQrcodeId())
                                     );

    QString dir = ( maindeal->navigator()->isForward() ) ? "前进" : "倒退";
    ui->label_gyro_position->setText( QString( " 导航模式:%1 设定方向:%2 避障开关:%3" )
                                          .arg( maindeal->navigator()->getNavigationModeChinese( navimode ) )  //
                                          .arg( dir )                                                          //
                                          .arg( maindeal->navigator()->isObstacleFunctionEnable() ) );
    Navigator::SlamNaviStatus status = maindeal->navigator()->getSlamNaviStatus();
    ui->label_magent_state->setText( QString( " 导航状态:%3 " )
                                         //      QString(" pid:%1  传输开关:%2 对接模式:%3 ROS状态:%4")
                                         //                                      .arg(maindeal->navigator()->pidAdj(), 0, 'f', 2)
                                         //                                     .arg( maindeal->agvhardware()->isTranSmitEnable() )
                                         //                                      .arg(maindeal->navigator()->getCoordinateModeChinese(maindeal->navigator()->curMode()))
                                         .arg( maindeal->navigator()->getSlamNaviStatusChinese( status ) ) );
}

/**
 * @brief MainWindow::showGearMovie 齿轮动画展示
 * 传入两轮速度的参考值，gif播放与否取决于两轮是否有速度
 * @param leftSpeed 左轮速度
 * @param rightSpeed 右轮速度
 */
void MainWindow::showGearMovie( double leftSpeed, double rightSpeed ) {
    Q_UNUSED( leftSpeed )
    Q_UNUSED( rightSpeed )
    /*  qmovie_left_gear_->start();
   * qmovie_right_gear_->start();
   *
   * if ( static_cast< int >( leftSpeed ) != 0 ) { qmovie_left_gear_->setSpeed( 100 ); }
   * else {
   *    qmovie_left_gear_->setPaused( true );
   * }
   *
   * if ( static_cast< int >( rightSpeed ) != 0 ) { qmovie_right_gear_->setSpeed( 100 ); }
   * else {
   *    qmovie_right_gear_->setPaused( true );
   * }
   */
}

/**
 * @brief MainWindow::initWidget 窗口界面控件整体初始化
 */
void MainWindow::initWidget()  //  MainWindow 初始化窗口控件
{
    /******************************* 左轮动画 **********************************************/
    /*  qmovie_left_gear_ = new QMovie( ":/pic/src/movie/gear.gif" );
   * ui->label_left_gear->setMovie( qmovie_left_gear_ );
   * qmovie_left_gear_->start();
   * qmovie_left_gear_->setPaused( true );
   * ui->label_left_gear->setScaledContents( true );*/
    /******************************* 右轮动画 **********************************************/
    /* qmovie_right_gear_ = new QMovie( ":/pic/src/movie/gear.gif" );
   * ui->label_right_gear->setMovie( qmovie_right_gear_ );
   * qmovie_right_gear_->start();
   * qmovie_right_gear_->setPaused( true );
   * ui->label_right_gear->setScaledContents( true );
   */
    /****************************** UI文本框颜色 *******************************************/
    //    black_style_ = QString("color:black;border-image: url(:/pic/src/other/金属.png);");
    //    white_style_ = QString("color:white;border-image: url(:/pic/src/other/金属.png);");
    //    red_style_ = QString("color:red;border-image: url(:/pic/src/other/金属.png);");
    //    green_style_ = QString("color:green;border-image: url(:/pic/src/other/金属.png);");
    //    orange_style_ = QString("color:orange;border-image: url(:/pic/src/other/金属.png);");
    black_style_  = QString( "color:black;" );
    white_style_  = QString( "color:white;" );
    red_style_    = QString( "color:red;" );
    green_style_  = QString( "color:green;" );
    orange_style_ = QString( "color:orange;" );
    /************************ button初始化 **************************************************/
    // 转角电机（左右）支持长按
    ui->pushButton_left->setAutoRepeat( true );
    ui->pushButton_left->setAutoRepeatDelay( 400 );
    ui->pushButton_left->setAutoRepeatInterval( 100 );
    ui->pushButton_right->setAutoRepeat( true );
    ui->pushButton_right->setAutoRepeatDelay( 400 );
    ui->pushButton_right->setAutoRepeatInterval( 100 );
}

/**
 * @brief MainWindow::sltShowTips 槽函数,用于显示状态信息.
 * @param tips 信息
 */
void MainWindow::sltShowTips( const QString& tips ) {
    if ( tips == "" ) { return; }
    static QString last_tips;

    if ( tips != last_tips ) {
        last_tips = tips;
        QString s = " " + QDateTime::currentDateTime().toString( "hh:mm:ss " ) + tips;

        if ( tips.left( 2 ) == QString( "错误" ) ) {
            qCritical() << tips;
            ui->textBrowser->append( "<font color=\"red\">" + s + "</font>" );    // 换行添加字符
            ui->textBrowser_2->append( "<font color=\"red\">" + s + "</font>" );  // 换行添加字符
            ui->textBrowser_3->append( "<font color=\"red\">" + s + "</font>" );  // 换行添加字符
        }
        else if ( tips.left( 2 ) == QString( "警告" ) ) {
            qDebug() << tips;
            ui->textBrowser->append( "<font color=\"Orange\">" + s + "</font>" );    // 换行添加字符
            ui->textBrowser_2->append( "<font color=\"Orange\">" + s + "</font>" );  // 换行添加字符
            ui->textBrowser_3->append( "<font color=\"Orange\">" + s + "</font>" );  // 换行添加字符
        }
        else {
            qDebug() << tips;
            ui->textBrowser->append( "<font color=\"blue\">" + s + "</font>" );    // 换行添加字符
            ui->textBrowser_2->append( "<font color=\"blue\">" + s + "</font>" );  // 换行添加字符
            ui->textBrowser_3->append( "<font color=\"blue\">" + s + "</font>" );  // 换行添加字符
        }

        ui->textBrowser->moveCursor( QTextCursor::End );
        ui->textBrowser_2->moveCursor( QTextCursor::End );
        ui->textBrowser_3->moveCursor( QTextCursor::End );
    }
}

/**
 * @brief MainWindow::sltShowSystemOrder 槽函数,用于显示调度系统的数据.
 * @param tips    信息头
 * @param string  信息内容
 */
void MainWindow::sltShowSystemOrder( const QByteArray& tips, const QString& string ) {
    if ( !is_show_system_order ) { return; }
    QString strDisplay = Utility::QbyteArry2Qstring( tips );
    QString s          = QDateTime::currentDateTime().toString( "hh:mm:ss " ) + string + "“" + strDisplay + "”";
    qDebug() << "------------systemdata-----------";
    qDebug() << s;
    qDebug() << "---------------------------------";
    ui->textBrowser_system_order->append( "<font color=\"blue\">" + s + "</font>" );  // append：换行添加字符
    ui->textBrowser_system_order->moveCursor( QTextCursor::End );
}

/**
 * @brief MainWindow::sltShowTemporaryOrder    槽函数,用于显示数据.
 * @param tips      信息头
 * @param string    信息内容
 */
void MainWindow::sltShowTemporaryOrder( const QByteArray& tips, const QString& string ) {
    if ( !is_show_system_order ) { return; }
    QString strDisplay = Utility::QbyteArry2Qstring( tips );
    QString s          = QDateTime::currentDateTime().toString( "hh:mm:ss:zzz " ) + string + "“" + strDisplay + "”";
    if ( string.left( 4 ) == "Send" ) {
        ui->textBrowser_wireless_order->append( "<font color=\"green\">" + s + "</font>" );  // append：换行添加字符
    }
    else {
        ui->textBrowser_wireless_order->append( "<font color=\"blue\">" + s + "</font>" );  // append：换行添加字符
    }

    ui->textBrowser_wireless_order->moveCursor( QTextCursor::End );
}

// void MainWindow::sltRecvKeyEvent( QKeyEvent* e ) {
//    keyPressEvent( e );
//}

void MainWindow::sltShowSysTips( const QString& ip, const QString& tips ) {
    ui->label_sys_websocket->setText( QDateTime::currentDateTime().toString( "hh:mm:ss " ) + tips );
    //显示ip
    ui->label_agv_line->setText( "调度IP:[" + ip + "]" );
}
void MainWindow::sltChangeModule( int index ) {
    ui->textBrowser_wireless_order->clear();
    disconnect( maindeal->agvhardware()->ioModule(), SIGNAL( sigIOReceiveData( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );
    disconnect( maindeal->agvhardware()->Battery_Ropeencoder_canModule(), SIGNAL( sigShowCanData( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );
    disconnect( maindeal, SIGNAL( sigSendSystemOrder( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );
    disconnect( maindeal->agvhardware()->canModule(), SIGNAL( sigShowCanData( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );
    disconnect( maindeal->agvhardware()->chargingPileModule(), SIGNAL( sigShowTCPData( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );

    switch ( index ) {
    case 1:
        connect( maindeal->agvhardware()->ioModule(), SIGNAL( sigIOReceiveData( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );
        break;
    case 2:
        connect( maindeal, SIGNAL( sigSendSystemOrder( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );
        break;
    case 3:
        connect( maindeal->agvhardware()->canModule(), SIGNAL( sigShowCanData( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );
        break;
    case 4:
        connect( maindeal->agvhardware()->Battery_Ropeencoder_canModule(), SIGNAL( sigShowCanData( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );
        break;
    case 5:
        connect( maindeal->agvhardware()->chargingPileModule(), SIGNAL( sigShowTCPData( QByteArray, QString ) ), this, SLOT( sltShowTemporaryOrder( QByteArray, QString ) ) );
        break;
    default:
        break;
    }
    //    case 2: connect(maindeal->agvhardware()->batteryModule(), SIGNAL(sigShowCanData(QByteArray, QString)), this, SLOT(sltShowTemporaryOrder(QByteArray, QString))); break;

    //        << "选择需要监控的模块"
    //        << "IO模块数据"
    //        << "发送调度数据"
    //        << "CAN报文数据";
    //        << "充电桩数据";
    //        << "电池模块数据"
}

/**
 * @brief MainWindow::on_pushButton_forward_clicked 槽函数,向前的事件
 */
void MainWindow::on_pushButton_forward_clicked() {
    double speed = 0.1;

    if ( !maindeal->navigator()->isAutoModeEnable() ) {
        double tmp = std::max( std::min( maindeal->navigator()->get_Fork_WalkSettingSpeed() + speed, 0.3 ), -0.3 );
        maindeal->navigator()->setForkWalksettingspeed( tmp );
    }
    sltShowTips( " 人工操作-前进!" );
}

/**
 * @brief MainWindow::on_pushButton_backward_clicked 槽函数,向后的事件
 */

void MainWindow::on_pushButton_backward_clicked() {
    double speed = 0.1;

    if ( !maindeal->navigator()->isAutoModeEnable() ) {
        double tmp = std::max( std::min( maindeal->navigator()->get_Fork_WalkSettingSpeed() - speed, 0.3 ), -0.3 );
        maindeal->navigator()->setForkWalksettingspeed( tmp );
    }
    sltShowTips( " 人工操作-后退!" );
}

/**
 * @brief MainWindow::on_pushButton_left_clicked 槽函数,左转的事件
 */
void MainWindow::on_pushButton_left_clicked() {
    static const double rad = M_PI / 12;

    if ( !maindeal->navigator()->isAutoModeEnable() ) {
        double angle = std::max( std::min( maindeal->navigator()->get_Fork_VeerSettingRad() + rad, M_PI_2 ), -M_PI_2 );
        maindeal->navigator()->setForkVeersettingrad( angle );
    }
    sltShowTips( " 人工操作-左转!" );
}

/**
 * @brief MainWindow::on_pushButton_right_clicked 槽函数,右转的事件
 */
void MainWindow::on_pushButton_right_clicked() {
    static const double rad = M_PI / 12;

    if ( !maindeal->navigator()->isAutoModeEnable() ) {
        double angle = std::max( std::min( maindeal->navigator()->get_Fork_VeerSettingRad() - rad, M_PI_2 ), -M_PI_2 );
        maindeal->navigator()->setForkVeersettingrad( angle );
    }
    sltShowTips( " 人工操作-右转!" );
}

/**
 * @brief MainWindow::on_pushButton_stop_clicked 槽函数,停止按键的事件
 */
void MainWindow::on_pushButton_stop_clicked() {
    if ( !maindeal->navigator()->isAutoModeEnable() ) {
        maindeal->navigator()->setForkWalksettingspeed( 0 );
        maindeal->navigator()->setForkVeersettingrad( 0 );
    }
}

/**
 * @brief MainWindow::on_pushButton_mode_change_clicked 连接到自动模式切换函数
 */
void MainWindow::on_pushButton_mode_change_clicked() {
    maindeal->navigator()->sltStartButtonHit();
}

/**
 * @brief MainWindow::on_pushButton_reset_state_clicked 复位所有状态，会恢复到开机的初始状态，谨慎使用
 */
void MainWindow::on_pushButton_reset_state_clicked() {
    int result = QMessageBox::question( this, "复位状态操作注意：", "<font size='30'>是否要将AGV进行状态复位？</font>", QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton );

    if ( result != QMessageBox::Yes ) { return; }

    maindeal->navigator()->clearEmergencyState();
    sltShowTips( tr( "清除异常状态成功!" ) );
    maindeal->navigator()->resetAllSwitchAndParam();
    maindeal->agvhardware()->resetHardwareState();
    maindeal->agvhardware()->stopCharge();
    maindeal->setCancelMission( false );  //复位任务清除状态
    sltShowTips( "关闭且复位所有模式和状态成功!" );
}

/**
 * @brief MainWindow::checkAndSetAuthority 检查用户权限,设置对应权限的功能
 * @param authority
 */
void MainWindow::checkAndSetAuthority() {
    // TODO //用户登录权限
    switch ( user_.authority() ) {
    case User::NOT_LOGIN:
        ui->tabWidget->setTabEnabled( 0, true );
        ui->tabWidget->setTabEnabled( 1, true );
        ui->tabWidget->setTabEnabled( 2, false );
        ui->tabWidget->setTabEnabled( 3, false );
        ui->tabWidget->setTabEnabled( 4, false );
        ui->tabWidget->setTabEnabled( 5, false );
        ui->tabWidget->setTabEnabled( 6, false );

        ui->pushButton_login->setDisabled( false );
        ui->pushButton_set_goal->setDisabled( true );
        ui->pushButton_cancel_goal->setDisabled( true );
        ui->pushButton_reset_state->setDisabled( true );
        ui->pushButton_mode_change->setDisabled( true );
        ui->pushButton_init_pose->setDisabled( true );
        ui->pushButton_clean_msg->setDisabled( true );
        ui->comboBox_chooese_hardware->setCurrentIndex( 0 );  //  4.14 登出设置模块数据combox index=0
        break;
    case User::SUPER_ADMIN:
        ui->tabWidget->setTabEnabled( 0, true );
        ui->tabWidget->setTabEnabled( 1, true );
        ui->tabWidget->setTabEnabled( 2, true );
        ui->tabWidget->setTabEnabled( 3, true );
        ui->tabWidget->setTabEnabled( 4, true );
        ui->tabWidget->setTabEnabled( 5, true );
        ui->tabWidget->setTabEnabled( 6, true );
        ui->pushButton_set_goal->setDisabled( false );
        ui->pushButton_cancel_goal->setDisabled( false );
        ui->pushButton_reset_state->setDisabled( false );
        ui->pushButton_mode_change->setDisabled( false );
        ui->pushButton_init_pose->setDisabled( false );
        ui->pushButton_clean_msg->setDisabled( false );
        break;
    case User::DEBUG_MODE:
        ui->tabWidget->setTabEnabled( 0, true );
        ui->tabWidget->setTabEnabled( 1, true );
        ui->tabWidget->setTabEnabled( 2, true );
        ui->tabWidget->setTabEnabled( 3, true );
        ui->tabWidget->setTabEnabled( 4, true );
        ui->tabWidget->setTabEnabled( 5, true );
        ui->tabWidget->setTabEnabled( 6, true );
        ui->pushButton_set_goal->setDisabled( false );
        ui->pushButton_cancel_goal->setDisabled( false );
        ui->pushButton_reset_state->setDisabled( false );
        ui->pushButton_mode_change->setDisabled( false );
        ui->pushButton_init_pose->setDisabled( false );
        ui->pushButton_clean_msg->setDisabled( false );
        break;
    }
}
/**
 * @brief MainWindow::on_pushButton_man_charge_clicked 槽函数，进行手动充电操作
 */
void MainWindow::on_pushButton_man_charge_clicked() {
    int result = QMessageBox::question( this, "充电操作注意：", "<font size='30'>AGV是否在充电桩旁边并且电杆对接准确？</font>", QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton );

    if ( result != QMessageBox::Yes ) { return; }
    sltShowTips( " 人工操作-强制伸出充电桩!" );

    maindeal->agvhardware()->startCharge();
}

/**
 * @brief MainWindow::on_pushButton_man_charge_return_clicked 槽函数，进行手动充电桩收回操作操作
 */
void MainWindow::on_pushButton_man_charge_return_clicked() {
    int result = QMessageBox::question( this, "收回充电桩操作：", "<font size='30'>此操作后强制收回充电桩，请注意是否安全？</font>", QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton );

    if ( result != QMessageBox::Yes ) { return; }
    sltShowTips( " 人工操作-强制收回充电桩!" );

    maindeal->agvhardware()->stopCharge();
}

/**
 * @brief MainWindow::on_pushButton_set_goal_clicked 界面输入设定终点
 */
void MainWindow::on_pushButton_set_goal_clicked() {
    //界面输入设定终点 界面显示文字缩小到15
    EndPoint endpoint( this );
    endpoint.setWindowTitle( "人工操作-设定目标" );
    endpoint.readConfig();
    int n = endpoint.exec();
    if ( n == QDialog::Accepted ) {
        if ( endpoint.isSetRoute() ) {
            int route = endpoint.getSelectRoute();
            if ( route > 0 ) {
                sltShowTips( QString( "人工操作-设定路线:%1" ).arg( route ) );
                maindeal->navigator()->SystemNaviOrder( route, false );  // 路线设置
            }
        }
        else {
            int id = endpoint.getSelectPoint();
            if ( id > 0 ) {
                QString                      machinemsg     = maindeal->navigator()->slamRoutePlanning()->getMachineMsg( id );  //  获取机台文字信息
                SlamRoutePlanning::RouteType route_type     = endpoint.routeType();
                QString                      route_type_msg = route_type == SlamRoutePlanning::ENTER ? "进入站点" : route_type == SlamRoutePlanning::EXIT ? "退出站点" : "一般路线";
                sltShowTips( QString( "人工操作-设定终点:%1(%2,%3)" ).arg( id ).arg( machinemsg ).arg( route_type_msg ) );  //  人工操作-设定终点 加入文字显示

                if ( maindeal->navigator()->isAllowSetRoute() ) {
                    maindeal->navigator()->SystemNaviOrder2( id, route_type, false );
                    //          QList<int> route_list;
                    //          route_list = maindeal->navigator()->slamRoutePlanning()->getSlamRoute(id);
                    //          if (route_list.isEmpty()) {  //空
                    //            sltShowTips(QString("警告，设定终点ID:%1失败，不存在该终点！").arg(id));
                    //          } else {
                    //            //打印筛选出来的路线号
                    //            //                    QString srting_route_list;
                    //            //                    foreach ( int route, route_list ) { srting_route_list += QString::number( route ) + " "; }
                    //            //                    sltShowTips( QString( "提示，设定终点ID:%1成功，检索出以下合适的路线号：" ).arg( id ) + srting_route_list );
                    //            sltShowTips("设定终点成功,自动检索合适路线。");
                    //            //用当前id坐标与每条路线比较
                    //            int excute_err = 99999;
                    //            int excute_route = 0;
                    //            foreach (int route, route_list) {
                    //              int cur_err;
                    //              bool result = maindeal->navigator()->inOnRoute(route, cur_err);
                    //              // UPDATE 2022.09.04 利用最小偏差发送最合适路线
                    //              if (result) {
                    //                if (cur_err < excute_err) {
                    //                  excute_err = cur_err;
                    //                  excute_route = route;
                    //                }
                    //              }
                    //            }
                    //            if (excute_err != 99999) {
                    //              sltShowTips(QString("提示，检索成功，合适的路线轨道:%2号线").arg(excute_route));
                    //              maindeal->navigator()->SystemNaviOrder(excute_route, false);
                    //            } else {
                    //              //以上循环全部检测，没有成功检索到合适路线
                    //              sltShowTips(QString("警告，检索失败，无合适的路线轨道！"));
                    //            }
                    //          }
                }
            }
        }
    }
}

void MainWindow::closeEvent( QCloseEvent* event ) {
    int result = QMessageBox::question( this, "提示", "<font size='30'>是否要关闭AGV控制系统</font>", QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton );

    if ( result == QMessageBox::Yes ) {
        maindeal->agvhardware()->waitingCloseAgv();
        Utility::blockDelayMs( 1500 );  // 延时等待
        event->accept();
    }
    else {
        event->ignore();
    }
}

void MainWindow::on_pushButton_window_fullscreen_clicked() {
    this->showFullScreen();
}

void MainWindow::on_pushButton_window_normal_clicked() {
    this->showNormal();
}

void MainWindow::on_pushButton_close_clicked() {
    this->close();
    maindeal->agvhardware()->canModule()->CanStopAndClose();
}

void MainWindow::on_pushButton_parm_reset_clicked() {
    UserInputUi password( User( "管理员", "486711" ), this );
    password.exec();
    if ( password.isOK() ) {
        ParameterSetting parameter_setting( this );
        parameter_setting.setWindowTitle( "参数设置" );
        parameter_setting.exec();
        if ( parameter_setting.isOK() ) {
            sltShowTips( "参数初始化成功!" );
            Param::readParam();
            //      maindeal->navigator()->initPid();
        }
    }
}

void MainWindow::on_pushButton_log_read_clicked() {
    Readlog readlog( this );

    readlog.exec();
}
// UPDATE 2022.10.29 on_pushButton_drive_enable_clicked checked按钮内容修改
void MainWindow::on_pushButton_drive_enable_clicked( bool checked ) {
    Q_UNUSED( checked )
}
// DELETE qnode relocation lockLocation
void MainWindow::on_pushButton_init_pose_clicked() {
    int result = QMessageBox::question( this, "重定位操作注意：", "<font size='30'>是否要重定位？</font>", QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton );
    if ( result == QMessageBox::Yes ) {
        Digitalkeyboard digi( this );
        digi.setWindowTitle( tr( "重定位点输入" ) );
        digi.exec();
        if ( digi.isOk() ) {
            int   id = digi.getTheNumber().toInt();
            Point point;
            bool  result = maindeal->navigator()->slamRoutePlanning()->getManInitPosePoint( id, point );
            if ( result ) {
                QString str = QString( "[x:%1 y:%2 th:%3]" ).arg( point.x() ).arg( point.y() ).arg( point.radian() );
                double  x   = point.x() / 1000.0;  //单位转换
                double  y   = point.y() / 1000.0;
                sltShowTips( "提示，解锁地图" );
                // UPDATE 2022.07.21 修改重定位
                maindeal->navigator()->lockLocalization( false );
                Utility::delayMs( 1000 );
                sltShowTips( "发布重定位点：" + QString::number( id ) + str );
                bool result2 = maindeal->navigator()->qnodeModule()->relocation( x, y, point.radian() );  // DELETE  relocation
                if ( result2 ) {
                    sltShowTips( "重定位成功：" + QString::number( id ) + str );
                    Utility::delayMs( 1000 );
                    sltShowTips( "提示，地图锁定" );
                    maindeal->navigator()->lockLocalization( true );
                }
                else {
                    sltShowTips( "ROS异常无法发布重定位点：" + QString::number( id ) );
                }
            }
            else {
                sltShowTips( "警告，无重定位点：" + QString::number( id ) );
            }
        }
    }
}

void MainWindow::on_pushButton_login_clicked() {
    UserInputUi user_input_ui( User( "设备员", "123", User::SUPER_ADMIN ), this );
    user_input_ui << User( "调试模式", "000", User::DEBUG_MODE );

    user_input_ui.exec();
    if ( user_input_ui.isOK() ) {
        user_ = user_input_ui.getLoginUser();
        checkAndSetAuthority();
        sltShowTips( "登录成功!" );
    }
}

void MainWindow::on_pushButton_logout_clicked() {
    user_ = User();
    checkAndSetAuthority();
    this->showFullScreen();
    sltShowTips( "退出成功!" );
}

void MainWindow::on_pushButton_usage_clicked() {
    QMessageBox::information( this, "AGV控制系统", "<font size='30'>使用前请登录为管理员，否则无法操作。\n详细操作请查看AGV控制系统操作手册。</font>" );
}

void MainWindow::on_pushButton_record_slam_point_clicked() {
    RecordSlamRoute record_slam_route( this );

    is_open_record_route = true;
    record_slam_route.setWindowTitle( "坐标录制" );
    //    connect( &record_slam_route, SIGNAL( sigSendKyeEvent( QKeyEvent* ) ), this, SLOT( sltRecvKeyEvent( QKeyEvent* ) ) );
    connect( this, SIGNAL( sigSendCurPoint( const int&, const int&, const double& ) ), &record_slam_route, SLOT( sltRecvCurPoint( const int&, const int&, const double& ) ) );
    connect( this, SIGNAL( sigSendQrocdePoint( const Point&, const int& ) ), &record_slam_route, SLOT( sltRecvQrcodePoint( const Point&, const int& ) ) );
    record_slam_route.exec();
    is_open_record_route = false;
}
void MainWindow::on_pushButton_creat_slamroutemsg_clicked() {
    maindeal->navigator()->slamRoutePlanning()->creatSlamRouteMsg();
}

void MainWindow::on_pushButton_read_route_clicked() {
    int result = QMessageBox::question( nullptr, "重新读取路线文件：", "<font size='30'>是否需要重新读取路线文件</font>", QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton );

    if ( result == QMessageBox::Yes ) {
        sltShowTips( "工具:重新读取激光路线文件!" );
        maindeal->navigator()->slamRoutePlanning()->allFileReset();
    }
}
// UPDATE 2022.10.29 pushButton_system_order_suspend checked按钮内容修改
void MainWindow::on_pushButton_system_order_suspend_clicked( bool checked ) {
    if ( checked ) {
        is_show_system_order = true;
        ui->pushButton_system_order_suspend->setText( "打开显示" );
    }
    else {
        is_show_system_order = false;
        ui->pushButton_system_order_suspend->setText( "暂停显示" );
    }
}

void MainWindow::on_pushButton_clean_msg_clicked() {
    ui->textBrowser->clear();
    ui->textBrowser_2->clear();
    ui->textBrowser_3->clear();
}

/**
 * @brief MainWindow::on_btn_fork_high_upper_clicked 界面货叉上升按钮
 */
void MainWindow::on_btn_fork_high_upper_clicked() {
    if ( maindeal->navigator()->isEmergencyStop() ) { sltShowTips( " 急停状态-无法上升货叉 " ); }
    else if ( maindeal->navigator()->isAutoModeEnable() ) {
        sltShowTips( " 非手动模式-无法上升货叉 " );
    }
    else {
        maindeal->agvhardware()->setForkliftState( Agvhardware::RAISE );
        sltShowTips( " 人工操作-货叉上升 " );
    }
}

/**
 * @brief MainWindow::on_btn_fork_low_down_clicked 界面货叉下降按钮
 */
void MainWindow::on_btn_fork_low_down_clicked() {
    if ( maindeal->navigator()->isEmergencyStop() ) { sltShowTips( " 急停状态-无法下降货叉 " ); }
    else if ( maindeal->navigator()->isAutoModeEnable() ) {
        sltShowTips( " 非手动模式-无法下降货叉 " );
    }
    else {
        maindeal->agvhardware()->setForkliftState( Agvhardware::FALL );
        sltShowTips( " 人工操作-货叉下降  " );
    }
}

/**
 * @brief MainWindow::on_btn_fork_HH_upper_clicked 界面货叉停止按钮
 */
void MainWindow::on_btn_fork_stop_clicked() {
    if ( maindeal->navigator()->isAutoModeEnable() ) {
        maindeal->navigator()->setEmergencyStop( true );
        sltShowTips( " 人工操作-停止货叉，不建议自动模式下手动操作，已切换至急停 " );
    } /*else {*/
    //      sltShowTips(" 人工操作-停止货叉 ");
    //    }
    maindeal->agvhardware()->setForkliftState( Agvhardware::STOP );
    sltShowTips( " 人工操作-停止货叉 " );
}

void MainWindow::on_pushButton_clear_system_order_clicked() {
    ui->textBrowser_system_order->clear();
    ui->textBrowser_wireless_order->clear();
}

void MainWindow::on_pushButton_reset_servo_alarm_clicked() {
    int result = QMessageBox::question( nullptr, "强制操作：", "<font size='30'>此功能无效</font>", QMessageBox::Yes, QMessageBox::No, QMessageBox::NoButton );

    if ( result == QMessageBox::Yes ) {
        sltShowTips( " 人工操作-请手动操作!" );  //  4.6 清除伺服报警
                                                 //        maindeal->agvhardware()->resetServoDriverAlarm();
    }
}

// UPDATE 2022.06.06 取消任务按钮
void MainWindow::on_pushButton_cancel_goal_clicked() {
    int result = QMessageBox::question( nullptr, "强制操作：", "<font size='30'>执行该操作后，小车会被调度判断为下线，需要点击复位状态按钮恢复上线，是否确定操作?</font>", QMessageBox::Yes,
                                        QMessageBox::No, QMessageBox::NoButton );

    if ( result == QMessageBox::Yes ) {
        sltShowTips( " 人工操作-取消调度任务!" );
        maindeal->setCancelMission( true );
    }
}
